import aiohttp
import json
import asyncio
from typing import Dict, Any, Optional, List
from decimal import Decimal

from .ai_base import BaseAIService, GenerationRequest, GenerationResponse, AIServiceProvider, AIServiceType
from app.core.error_handler import robust_executor, with_retry

class RunwayMLService(BaseAIService):
    """Runway ML AI服务实现"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.api_key = config.get("api_key")
        
        # API URLs
        self.base_url = "https://api.runwayml.com/v1"
        self.video_generation_url = f"{self.base_url}/image_to_video"
        self.text_to_video_url = f"{self.base_url}/gen3/text_to_video"
        self.tasks_url = f"{self.base_url}/tasks"
        
        # Runway ML特殊配置
        self.max_duration = config.get("max_duration", 10)  # 最大视频时长(秒)
        self.default_resolution = config.get("default_resolution", "1280x768")
        self.model_name = config.get("model_name", "gen3a_turbo")
    
    def get_provider_name(self) -> AIServiceProvider:
        return AIServiceProvider.RUNWAY_ML
    
    def get_supported_services(self) -> List[AIServiceType]:
        return [
            AIServiceType.VIDEO_GENERATION,
        ]
    
    async def generate_video(self, request: GenerationRequest) -> GenerationResponse:
        """生成视频内容 - 带错误处理和重试"""
        return await robust_executor.execute_with_retry(
            self._generate_video_internal,
            "runway_ml",
            request
        )
    
    async def _generate_video_internal(self, request: GenerationRequest) -> GenerationResponse:
        """内部视频生成方法"""
        try:
            # 构建请求参数
            payload = self._build_video_generation_payload(request)
            
            # 确定使用的API端点
            api_url = self.text_to_video_url
            if request.params.get("source_image_url"):
                api_url = self.video_generation_url
            
            # 发送请求到Runway ML API
            async with aiohttp.ClientSession() as session:
                headers = {
                    "Authorization": f"Bearer {self.api_key}",
                    "Content-Type": "application/json"
                }
                
                async with session.post(
                    api_url,
                    headers=headers,
                    json=payload,
                    timeout=aiohttp.ClientTimeout(total=300)  # 5分钟超时
                ) as response:
                    
                    if response.status == 200:
                        result = await response.json()
                        return await self._process_video_generation_response(result, request)
                    elif response.status == 429:
                        # API限流错误
                        raise Exception("Rate limit exceeded - too many requests")
                    elif response.status == 401:
                        # 认证错误
                        raise Exception("Authentication failed - invalid API key")
                    elif response.status == 402:
                        # 余额不足
                        raise Exception("Payment required - insufficient credits")
                    elif response.status >= 500:
                        # 服务器错误
                        error_text = await response.text()
                        raise Exception(f"Service unavailable - server error {response.status}: {error_text}")
                    else:
                        error_text = await response.text()
                        raise Exception(f"Runway ML API错误: {response.status} - {error_text}")
                        
        except asyncio.TimeoutError:
            raise Exception("Request timeout - Runway ML generation took too long")
        except aiohttp.ClientError as e:
            raise Exception(f"Network error - connection failed: {str(e)}")
        except Exception as e:
            # 重新抛出，让错误处理器分类
            raise e
    
    def _build_video_generation_payload(self, request: GenerationRequest) -> Dict[str, Any]:
        """构建视频生成请求payload"""
        
        # 基础参数
        payload = {
            "model": self.model_name,
            "prompt": request.prompt,
            "duration": min(request.params.get("duration", 5), self.max_duration),
            "ratio": request.params.get("aspect_ratio", "16:9"),
            "seed": request.params.get("seed"),
        }
        
        # 移除None值
        payload = {k: v for k, v in payload.items() if v is not None}
        
        # 如果有源图片（图片转视频）
        if request.params.get("source_image_url"):
            payload["promptImage"] = request.params["source_image_url"]
            # 图片转视频的特殊参数
            if request.params.get("motion_bucket"):
                payload["motionBucket"] = request.params["motion_bucket"]
        
        # 运动强度参数
        motion_strength = request.params.get("motion", "medium")
        motion_values = {"low": 5, "medium": 10, "high": 18}
        if motion_strength in motion_values:
            payload["motionBucket"] = motion_values[motion_strength]
        
        return payload
    
    async def _process_video_generation_response(self, result: Dict[str, Any], request: GenerationRequest) -> GenerationResponse:
        """处理视频生成响应"""
        try:
            # Runway ML返回任务ID，需要轮询获取结果
            task_id = result.get("id")
            if not task_id:
                return GenerationResponse(
                    success=False,
                    error_message="Runway ML未返回任务ID"
                )
            
            # 等待任务完成
            video_url = await self._wait_for_task_completion(task_id)
            
            if not video_url:
                return GenerationResponse(
                    success=False,
                    error_message="视频生成失败或超时"
                )
            
            # 构建成功响应
            response = GenerationResponse(
                success=True,
                result_url=video_url,
                metadata={
                    "task_id": task_id,
                    "duration": request.params.get("duration", 5),
                    "model": self.model_name,
                    "provider": "runway_ml",
                    "aspect_ratio": request.params.get("aspect_ratio", "16:9"),
                    "motion_strength": request.params.get("motion", "medium"),
                    "seed": request.params.get("seed")
                },
                cost=self.calculate_cost(request)
            )
            
            return response
            
        except Exception as e:
            return GenerationResponse(
                success=False,
                error_message=f"处理Runway ML响应失败: {str(e)}"
            )
    
    async def _wait_for_task_completion(self, task_id: str, max_wait_time: int = 300) -> Optional[str]:
        """等待任务完成并获取结果"""
        wait_time = 0
        check_interval = 10  # 每10秒检查一次
        
        while wait_time < max_wait_time:
            try:
                status = await self.get_task_status(task_id)
                
                if status == "completed":
                    # 获取任务详情
                    task_detail = await self._get_task_detail(task_id)
                    if task_detail and task_detail.get("output"):
                        return task_detail["output"][0] if isinstance(task_detail["output"], list) else task_detail["output"]
                elif status == "failed":
                    return None
                
                # 等待下一次检查
                await asyncio.sleep(check_interval)
                wait_time += check_interval
                
            except Exception:
                # 如果状态查询失败，继续等待
                await asyncio.sleep(check_interval)
                wait_time += check_interval
        
        return None
    
    async def _get_task_detail(self, task_id: str) -> Optional[Dict[str, Any]]:
        """获取任务详情"""
        try:
            async with aiohttp.ClientSession() as session:
                headers = {
                    "Authorization": f"Bearer {self.api_key}",
                    "Content-Type": "application/json"
                }
                
                async with session.get(
                    f"{self.tasks_url}/{task_id}",
                    headers=headers,
                    timeout=aiohttp.ClientTimeout(total=30)
                ) as response:
                    
                    if response.status == 200:
                        return await response.json()
                    
        except Exception:
            pass
        
        return None
    
    async def generate_image(self, request: GenerationRequest) -> GenerationResponse:
        """Runway ML主要专注于视频生成"""
        return GenerationResponse(
            success=False,
            error_message="Runway ML专注于视频生成，图片生成请使用其他服务"
        )
    
    async def edit_image(self, request: GenerationRequest, image_url: str) -> GenerationResponse:
        """图片编辑不支持"""
        return GenerationResponse(
            success=False,
            error_message="Runway ML不支持图片编辑功能"
        )
    
    async def face_swap(self, request: GenerationRequest, source_url: str, target_url: str) -> GenerationResponse:
        """人脸替换不支持"""
        return GenerationResponse(
            success=False,
            error_message="Runway ML不支持人脸替换功能"
        )
    
    async def get_task_status(self, task_id: str):
        """获取任务状态"""
        return await robust_executor.execute_with_retry(
            self._get_task_status_internal,
            "runway_ml_status",
            task_id
        )
    
    async def _get_task_status_internal(self, task_id: str) -> str:
        """内部任务状态查询方法"""
        try:
            async with aiohttp.ClientSession() as session:
                headers = {
                    "Authorization": f"Bearer {self.api_key}",
                    "Content-Type": "application/json"
                }
                
                async with session.get(
                    f"{self.tasks_url}/{task_id}",
                    headers=headers,
                    timeout=aiohttp.ClientTimeout(total=30)
                ) as response:
                    
                    if response.status == 200:
                        result = await response.json()
                        status = result.get("status", "unknown")
                        
                        # 映射Runway ML状态到标准状态
                        status_mapping = {
                            "PENDING": "processing",
                            "RUNNING": "processing", 
                            "SUCCEEDED": "completed",
                            "FAILED": "failed",
                            "CANCELLED": "failed"
                        }
                        
                        return status_mapping.get(status.upper(), "processing")
                    elif response.status == 404:
                        return "not_found"
                    else:
                        raise Exception(f"Status query failed: {response.status}")
        
        except Exception:
            # 如果状态查询失败，默认为处理中
            return "processing"
    
    def validate_config(self) -> bool:
        """验证配置是否正确"""
        try:
            if not self.api_key:
                return False
            
            if self.max_duration <= 0 or self.max_duration > 10:
                return False
            
            return True
        except Exception:
            return False
    
    def calculate_cost(self, request: GenerationRequest) -> float:
        """估算成本"""
        try:
            # Runway ML定价（基于实际API定价调整）
            base_cost = 0.05  # 每秒基础成本
            duration = request.params.get("duration", 5)
            
            if not isinstance(duration, (int, float)) or duration <= 0:
                duration = 5
            
            # 模型成本倍数
            model_multiplier = {
                "gen3a_turbo": 1.0,
                "gen3": 2.0,  # 更高质量但更贵
            }.get(self.model_name, 1.0)
            
            # 分辨率成本倍数
            aspect_ratio = request.params.get("aspect_ratio", "16:9")
            resolution_multiplier = {
                "1:1": 1.0,
                "16:9": 1.2,
                "9:16": 1.2,
                "4:3": 1.1,
                "3:4": 1.1
            }.get(aspect_ratio, 1.2)
            
            # 图片转视频成本更高
            mode_multiplier = 1.5 if request.params.get("source_image_url") else 1.0
            
            total_cost = (base_cost * duration * model_multiplier * 
                         resolution_multiplier * mode_multiplier)
            
            return round(total_cost, 3)
            
        except Exception:
            # 如果成本计算失败，返回默认成本
            return 0.25  # 默认估算成本